package com.zerro.litez.compiler.processor.parser;

import javax.lang.model.element.*;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 类解析器
 */
public abstract class AbstractClassParser {

    /**
     * 解析中的元素
     */
    TypeElement parsingElement;
    /**
     * 解析器支持的注解列表
     */
    final List<Class<? extends Annotation>> supportedAnnotation = new ArrayList<>();
    AbstractClassParser(TypeElement parsingElement) {
        this.parsingElement = parsingElement;
    }

    protected abstract void onParse();

    /**
     * 获取类元素的所有成员变量，根据名称排序后返回
     * @param element 元素
     * @return 变量元素数组
     */
    public List<VariableElement> getFields(TypeElement element, Checker extra) {
        return getEnclosedElements(element, ele -> ele.getKind().isField(), extra);
    }

    /**
     * 按照指定规则获取元素的方法
     * @param element 元素
     * @param extra 指定规则
     * @return 方法元素列表
     */
    public List<ExecutableElement> getMethods(Element element, Checker extra) {
        return getEnclosedElements(element, ele -> ele.getKind().equals(ElementKind.METHOD), extra);
    }
    /**
     * 按照指定规则获取元素的构造方法
     * @param element 元素
     * @param extra 指定规则
     * @return 方法元素列表
     */
    public List<ExecutableElement> getConstructors(Element element, Checker extra) {
        return getEnclosedElements(element, ele -> ele.getKind().equals(ElementKind.CONSTRUCTOR), extra);
    }

    /**
     * 检查器
     * 检查元素是否合规
     */
    public interface Checker {
        /**
         * 检查元素是否合规
         * @param ele 元素
         */
        boolean check(Element ele);
    }

    /**
     * 获取元素内的元素列表，可以指定类型和附加条件
     * @param element
     * @param types 类型检查器
     * @param extra 附加检查器
     * @return
     */
    public <T extends Element> List<T> getEnclosedElements(Element element, Checker types, Checker extra) {
        List<T> elements = new ArrayList<>();
        for (Element enclosedElement : element.getEnclosedElements()) {
            boolean ex = extra == null || extra.check(enclosedElement);
            if (types.check(enclosedElement) && ex) {
                elements.add((T) enclosedElement);
            }
        }
        return elements;
    }

    public List<String> splitData(String str) {
        if (str == null || !str.contains(", ")) {
            return new ArrayList<>();
        }
        return Arrays.asList(str.split(", "));
    }

    public TypeElement getParsingElement() {
        return parsingElement;
    }


    public String getElementPackage(){

        String fullName = parsingElement.toString();
        String name = parsingElement.getSimpleName().toString();

        return fullName.substring(0, fullName.length() - name.length() - 1);
    }
}
